Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Construct multi-qubit Pauli terms and sums from strings #984

Merged
merged 21 commits into from
Sep 20, 2019
Merged

Construct multi-qubit Pauli terms and sums from strings #984

merged 21 commits into from
Sep 20, 2019

Conversation

jlbosse
Copy link
Contributor

@jlbosse jlbosse commented Aug 31, 2019

Description

Added PauliSum.from_compact_str() and PauliSum.compact_str() in analogy to PauliTerm.from_compact_str() and PaulTerm.compact_str().
Added support for multi-qubit operators to PauliTerm.from_compact_str().
Added corresponding tests.

This resolves #977 mostly (except for the question, whether init() should also be changed).

Checklist

  • The above description motivates these changes.
  • There is a unit test that covers these changes.
  • All new and existing tests pass locally and on Semaphore.
    test_examples.test_quantum_die fails, but since I ddin't touch anything remotely related I
    am sure I can shift the blame
  • Parameters have type hints with PEP 484 syntax.
    None of the surrounding code has type hints, so I refrained from adding them.
  • Functions and classes have useful sphinx-style docstrings.
  • (New Feature) The docs have been updated accordingly.
  • (Bugfix) The associated issue is referenced above using
    auto-close keywords.
  • The changelog is updated,
    including author and PR number (@username, gh-xxx).

Added support for multi-qubit strings to PauliTerm.from_compact_str
Added Tests for all of the above
	changed:       pyquil/paulis.py
	changed:       pyquil/tests/test_paulis.py
	changed:       pyquil/tests/test_paulis_with_placeholders.py
	changed:       pyquil/tests/test_paulis.py
	changed:       pyquil/tests/test_paulis_with_placeholders.py
and the tests.

Made the syntactical changes requested by  @msohaibalam

Zum Commit vorgemerkte Änderungen:
	geändert:       pyquil/paulis.py
	geändert:       pyquil/tests/test_paulis.py
	geändert:       pyquil/tests/test_paulis_with_placeholders.py
Fixed the dimensions of the resulting matrices, s.t. it produces the
same results as pyquil.unitary_tools.lifted_pauli
 - Added PauliSum.from_compact_str()
 - Added support for multiqubit strings to PauliTerm.from_compact_str()
 - Added PauliSum.compact_str()

	changed:       paulis.py
	changed:       tests/test_paulis.py
	changed:       tests/test_paulis_with_placeholders.py
	changed:       CHANGELOG.md
	changed:       docs/source/apidocs/pauli.rst
@jlbosse jlbosse requested a review from a team as a code owner August 31, 2019 07:07
Copy link
Contributor

@appleby appleby left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small docstring typo. Otherwise looks reasonable to me.

pyquil/paulis.py Outdated Show resolved Hide resolved
Copy link
Contributor

@appleby appleby left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have an opinion on the __init__ question, but otherwise lgtm

Copy link
Contributor

@karalekas karalekas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jlbosse Thanks for the PR! I think the scope of this looks great. However, parsing magic strings can get a little hairy. For example, the following does not work:

In [21]: print(PauliSum.from_compact_str('X0 + X1'))
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-21-b4e8046c399b> in <module>
----> 1 print(PauliSum.from_compact_str('X0 + X1'))

~/code/rigetti/github/pyquil/pyquil/paulis.py in from_compact_str(cls, str_pauli_sum)
    704         str_terms = re.split(r'\+(?![^(]*\))', str_pauli_sum)
    705         str_terms = [s.strip() for s in str_terms]
--> 706         terms = [PauliTerm.from_compact_str(term) for term in str_terms]
    707         return cls(terms)
    708

~/code/rigetti/github/pyquil/pyquil/paulis.py in <listcomp>(.0)
    704         str_terms = re.split(r'\+(?![^(]*\))', str_pauli_sum)
    705         str_terms = [s.strip() for s in str_terms]
--> 706         terms = [PauliTerm.from_compact_str(term) for term in str_terms]
    707         return cls(terms)
    708

~/code/rigetti/github/pyquil/pyquil/paulis.py in from_compact_str(cls, str_pauli_term)
    359         """
    360         # split into coefficient and operator
--> 361         str_coef, str_op = str_pauli_term.split('*', maxsplit=1)
    362
    363         # parse the coefficient

ValueError: not enough values to unpack (expected 2, got 1)

Granted, this example is due to the fact that print(PauliTerm.from_compact_str('X0')) doesn't work, but there are other "reasonable-looking" strings that you can put in that spit out a confusing error. Thus, I think it would be best to expand upon your unit test, and include every flavor of compact string you can think of. For those that should actually result in an error, we should catch that and return an error message that gives the user a sense of what to do next. Let me know if that makes sense -- happy to add more detail.

Some additional examples:

  • this should probably error but doesn't
In [24]: print(PauliTerm.from_compact_str('1+1j*X0'))
(1+1j)*X0
  • this should probably collect terms
In [29]: print(PauliSum.from_compact_str("(1.0+0j)*X0 + (1.0+0j)*Y0 + 1*X0"))
(1+0j)*X0 + (1+0j)*Y0 + (1+0j)*X0
  • this should probably tell you "A" is the problem
In [30]: PauliSum.from_compact_str("(1.0+0j)*X0 + (1.0+0j)*A0")
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-30-e717615331a7> in <module>
----> 1 PauliSum.from_compact_str("(1.0+0j)*X0 + (1.0+0j)*A0")

~/code/rigetti/github/pyquil/pyquil/paulis.py in from_compact_str(cls, str_pauli_sum)
    704         str_terms = re.split(r'\+(?![^(]*\))', str_pauli_sum)
    705         str_terms = [s.strip() for s in str_terms]
--> 706         terms = [PauliTerm.from_compact_str(term) for term in str_terms]
    707         return cls(terms)
    708

~/code/rigetti/github/pyquil/pyquil/paulis.py in <listcomp>(.0)
    704         str_terms = re.split(r'\+(?![^(]*\))', str_pauli_sum)
    705         str_terms = [s.strip() for s in str_terms]
--> 706         terms = [PauliTerm.from_compact_str(term) for term in str_terms]
    707         return cls(terms)
    708

~/code/rigetti/github/pyquil/pyquil/paulis.py in from_compact_str(cls, str_pauli_term)
    378         str_op = re.sub(r'\*', '', str_op)
    379         if not re.match(r'^(([XYZ])(\d+))+$', str_op):
--> 380             raise ValueError(f"Could not parse pauli string {str_pauli_term}")
    381
    382         for factor in re.finditer(r'([XYZ])(\d+)', str_op):

ValueError: Could not parse pauli string (1.0+0j)*A0

Many (if not all) of these are errors that existed before you opened your PR, so if you'd rather just catch them in your tests as expected errors, open an issue, and leave them for a later PR (as to not blow up the scope of your work) that is totally fine with me. In fact, that might be the best course of action. Let me know what you think! 😃

@karalekas
Copy link
Contributor

As for __init__, let's leave that question for another PR. 👍

@jlbosse
Copy link
Contributor Author

jlbosse commented Sep 18, 2019

@karalekas Thanks for pointing out the manifold ways the user could specify unparsable strings. I added checks for each part of the string now that point at the exact part where things became unparsable.

  • For the case "(1.0+0j)*X0 + (1.0+0j)*Y0 + 1*X0" I added a simple .simplify() before return.
  • "(1.0+0j)*A0" and "X0 + X1" now throw more informative exceptions.
  • "1+1j*X0" is still considered equivalent to "(1+1j)*X0". Should I add another check, that '+' only shows up inside parenthesis when part of a pauli_term_str? Note, that this is only potentially confusing when passed to PauliTerm.from_compact_str(). PauliSum.from_compact_str(), on the other hand, will choke on "1+1j*X0" and raise a value error telling you what went wrong.

Copy link
Contributor

@karalekas karalekas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for adding all those tests! I think this is nearly ready, but I think that the test_pauli_sum_from_str could be expanded as well. Could you test a couple more pauli sum strings that should parse correctly, as well as test the simplification component?

 - Test that simplification works correctly
 - Test that PauliSums of length 1 work correctly
 - Test that strings containing the identity work correctly

Changed files:
	changed:       tests/test_paulis.py
@jlbosse
Copy link
Contributor Author

jlbosse commented Sep 19, 2019

Done! I added tests for the simplification part, strings containing the identity and sums of length one.

Copy link
Contributor

@karalekas karalekas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some small fixes to the changelog entry -- also please move it up to the "Improvements and Changes" section for v2.12, instead of v2.11. Probably want to rebase off of master too.

CHANGELOG.md Outdated Show resolved Hide resolved
CHANGELOG.md Outdated Show resolved Hide resolved
CHANGELOG.md Outdated Show resolved Hide resolved
jlbosse and others added 2 commits September 20, 2019 09:09
Co-Authored-By: Peter Karalekas <peter@rigetti.com>
Co-Authored-By: Peter Karalekas <peter@rigetti.com>
jlbosse and others added 2 commits September 20, 2019 09:10
Co-Authored-By: Peter Karalekas <peter@rigetti.com>
@jlbosse
Copy link
Contributor Author

jlbosse commented Sep 20, 2019

Incorporated the Changelog changes (and clearly didn't write that fact into the changelog, otherwise I would still be stuck in a recursive loop without exit condition). Rebasing off master has been done commit e761981.

@karalekas karalekas changed the title Added PauliSum.from_compact_str() and updated PaulTerm.from_compact_str() Construction of multi-qubit Pauli terms and sums from strings Sep 20, 2019
@karalekas karalekas changed the title Construction of multi-qubit Pauli terms and sums from strings Construct multi-qubit Pauli terms and sums from strings Sep 20, 2019
Copy link
Contributor

@karalekas karalekas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@karalekas
Copy link
Contributor

@jlbosse Congrats on your first PR contribution to pyQuil! :)

@karalekas karalekas merged commit 9bdbd71 into rigetti:master Sep 20, 2019
@karalekas karalekas added this to the v2.12 milestone Sep 20, 2019
@karalekas karalekas added the enhancement ✨ A request for a new feature. label Sep 20, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement ✨ A request for a new feature.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

PauliTerms can only be constructed on one qubit
3 participants